/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "vpn_cert_utils.h"

#include <string.h>
#include "debug.h"

void FreeCertList(struct CertList *certList)
{
    if (certList == NULL) {
        return;
    }

    if (certList->certAbstract != NULL) {
        free(certList->certAbstract);
        certList->certAbstract = NULL;
    }

    free(certList);
    certList = NULL;
}

void FreeCMBlobData(struct CmBlob *blob)
{
    if (blob == NULL) {
        return;
    }

    if (blob->data != NULL) {
        free(blob->data);
        blob->data = NULL;
    }
    blob->size = 0;
}

int32_t InitCertInfo(struct CertInfo *certInfo)
{
    if (certInfo == NULL) {
        return CMR_ERROR_MALLOC_FAIL;
    }

    certInfo->certInfo.data = (uint8_t *)(malloc(MAX_LEN_CERTIFICATE));
    if (certInfo->certInfo.data == NULL) {
        return CMR_ERROR_MALLOC_FAIL;
    }
    certInfo->certInfo.size = MAX_LEN_CERTIFICATE;

    return CM_SUCCESS;
}

void InitAppCertList(struct CredentialList *credentialList)
{
    uint32_t buffSize = (MAX_COUNT_CERTIFICATE * sizeof(struct CredentialAbstract));
    credentialList->credentialAbstract = (struct CredentialAbstract *)(malloc(buffSize));
    if (credentialList->credentialAbstract == NULL) {
        return;
    }
    (void)memset(credentialList->credentialAbstract, 0, buffSize);
    credentialList->credentialCount = MAX_COUNT_CERTIFICATE;
}

int32_t InitCertList(struct CertList **cList)
{
    *cList = (struct CertList *)(malloc(sizeof(struct CertList)));
    if (*cList == NULL) {
        DBG2(DBG_CFG, "rzc InitCertList failed");
        return CMR_ERROR_MALLOC_FAIL;
    }

    uint32_t buffSize = MAX_COUNT_CERTIFICATE * sizeof(struct CertAbstract);
    (*cList)->certAbstract = (struct CertAbstract *)(malloc(buffSize));
    if ((*cList)->certAbstract == NULL) {
        DBG2(DBG_CFG, "rzc InitCertList certAbstract failed");
        return CMR_ERROR_MALLOC_FAIL;
    }
    (void)memset_s((*cList)->certAbstract, buffSize, 0, buffSize);
    (*cList)->certsCount = MAX_COUNT_CERTIFICATE;

    return CM_SUCCESS;
}

int32_t GetSytemRootCertSum()
{
    int32_t certSum = -1;
    struct CertList *certList = NULL;
    int32_t ret = InitCertList(&certList);
    if (ret != CM_SUCCESS) {
        FreeCertList(certList);
        return certSum;
    }

    ret = CmGetCertList(CM_SYSTEM_TRUSTED_STORE, certList);
    if (ret != CM_SUCCESS) {
        FreeCertList(certList);
        return certSum;
    }

    if (certList != NULL) {
        certSum = certList->certsCount;
    }

    FreeCertList(certList);
    return certSum;
}

int32_t GetAppCertSum()
{
    int32_t certSum = -1;
    // struct CredentialList *certList = (struct CredentialList *)(malloc(sizeof(struct CredentialList)));
    // InitAppCertList(certList);
    // int32_t ret = CmGetAppCertList(CM_USER_TRUSTED_STORE, certList);
    // if (ret != CM_SUCCESS) {
    //     return certSum;
    // }

    // if (certList != NULL) {
    //     certSum = certList->credentialCount;
    // }
    return certSum;
}

int32_t GetCertInfo(char *uri, struct CertInfo *certInfo, int32_t certType)
{
    unsigned int len = sizeof(struct CertInfo);
    (void)memset(certInfo, 0, len);
    int32_t ret = InitCertInfo(certInfo);
    if (ret != CM_SUCCESS) {
        // NETMGR_EXT_LOG_E("GetCertInfo, init cert failed, ret = %d", ret);
        FreeCMBlobData(&(certInfo->certInfo));
        return CM_FAILURE;
    }

    struct CmBlob uriBlob = {strlen(uri) + 1, (uint8_t *)(uri)};

    if (certType == CM_USER_TRUSTED_STORE) {
        ret = CmGetUserCertInfo(&uriBlob, certType, certInfo);
    } else if (certType == CM_SYSTEM_TRUSTED_STORE) {
        ret = CmGetCertInfo(&uriBlob, certType, certInfo);
    } else {
        // NETMGR_EXT_LOG_E("GetCertInfo, unknow certType = %d", certType);
        FreeCMBlobData(&(certInfo->certInfo));
        return CM_FAILURE;
    }

    if (ret != CM_SUCCESS) {
        // NETMGR_EXT_LOG_E("GetCertInfo, get cert info failed, ret = %d", ret);
        FreeCMBlobData(&(certInfo->certInfo));
        return ret;
    }
    return CM_SUCCESS;
}

int32_t GetVpnUserCertSum()
{
    int32_t certSum = -1;
    struct CertList *certList = NULL;
    int32_t ret = InitCertList(&certList);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetVpnUserCertSum init cert list, ret = %d",ret);
        FreeCertList(certList);
        return certSum;
    }

    ret = CmGetUserCertList(CM_USER_TRUSTED_STORE, certList);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetVpnUserCertSum get user cert list faile, ret = %d",ret);
        FreeCertList(certList);
        return certSum;
    }

    if (certList != NULL) {
        certSum = certList->certsCount;
    }

    FreeCertList(certList);
    return certSum;
}

int32_t GetVpnUserCertData(uint32_t certCount, uint8_t* certData)
{
    struct CertList *certList = NULL;
    int32_t ret = InitCertList(&certList);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetVpnUserCertData init cert list, ret = %d",ret);
        FreeCertList(certList);
        return CM_FAILURE;
    }

    ret = CmGetUserCertList(CM_USER_TRUSTED_STORE, certList);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetVpnUserCertData get user cert list failed, ret = %d",ret);
        FreeCertList(certList);
        return CM_FAILURE;
    }

    struct CertInfo certInfo;
    unsigned int len = sizeof(struct CertInfo);
    (void)memset(&certInfo, 0, len);
    ret = InitCertInfo(&certInfo);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetVpnUserCertData get init cert info failed, ret = %d",ret);
        FreeCMBlobData(&(certInfo.certInfo));
        FreeCertList(certList);
        return CM_FAILURE;
    }

    if (certCount > certList->certsCount) {
        // NETMGR_EXT_LOG_E("GetUserRootCertData, cert count invailed, cert count = %d ", certCount);
        return CM_FAILURE;
    }

    for (size_t i = 0; i < certList->certsCount; i++)
    {
        DBG2(DBG_CFG, "rzc GetVpnUserCertData index = %d, uri = %s",i, certList->certAbstract[i].uri);
        DBG2(DBG_CFG, "rzc GetVpnUserCertData index = %d, certAlias = %s",i, certList->certAbstract[i].certAlias);
        DBG2(DBG_CFG, "rzc GetVpnUserCertData index = %d, subjectName = %s",i, certList->certAbstract[i].subjectName);
    }

    char *uri = certList->certAbstract[certCount].uri;
    struct CmBlob uriBlob = {strlen(uri) + 1, (uint8_t *)(uri)};

    ret = CmGetUserCertInfo(&uriBlob, CM_USER_TRUSTED_STORE, &certInfo);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetVpnUserCertData get user cert info failed, ret = %d",ret);
        FreeCMBlobData(&(certInfo.certInfo));
        FreeCertList(certList);
        return CM_FAILURE;
    }

    if (memcpy(certData, certInfo.certInfo.data, certInfo.certInfo.size) != 0) {
        // NETMGR_EXT_LOG_E("GetUserRootCertData, memory copy failed");
        FreeCMBlobData(&(certInfo.certInfo));
        FreeCertList(certList);
        return CM_FAILURE;
    }

    FreeCMBlobData(&(certInfo.certInfo));
    FreeCertList(certList);
    return CM_SUCCESS;
}

int32_t GetSytemRootCertData(int32_t certCount, uint8_t* certData)
{
    struct CertList *certList = NULL;
    int32_t ret = InitCertList(&certList);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetSytemRootCertData, init cert list failed, ret = %d ", ret);
        FreeCertList(certList);
        return CM_FAILURE;
    }

    ret = CmGetCertList(CM_SYSTEM_TRUSTED_STORE, certList);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetSytemRootCertData, get cert list failed, ret = %d ", ret);
        FreeCertList(certList);
        return CM_FAILURE;
    }

    DBG2(DBG_CFG, "rzc GetSytemRootCertData, get cert list count = %d", certList->certsCount);

    struct CertInfo certInfo;
    unsigned int len = sizeof(struct CertInfo);
    (void)memset_s(&certInfo, len, 0, len);
    ret = InitCertInfo(&certInfo);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetSytemRootCertData, init cert failed, ret = %d ", ret);
        FreeCMBlobData(&(certInfo.certInfo));
        FreeCertList(certList);
        return CM_FAILURE;
    }

    if (certCount > certList->certsCount) {
        DBG2(DBG_CFG, "rzc GetSytemRootCertData, cert count invailed, cert count = %d ", certCount);
        return CM_FAILURE;
    }

    for (size_t i = 0; i < certList->certsCount; i++)
    {
        DBG2(DBG_CFG, "rzc GetSytemRootCertData index = %d, uri = %s",i, certList->certAbstract[i].uri);
        DBG2(DBG_CFG, "rzc GetSytemRootCertData index = %d, certAlias = %s",i, certList->certAbstract[i].certAlias);
        DBG2(DBG_CFG, "rzc GetSytemRootCertData index = %d, subjectName = %s",i, certList->certAbstract[i].subjectName);
    }

    char *uri = certList->certAbstract[certCount].uri;
    struct CmBlob uriBlob = {strlen(uri) + 1, (uint8_t *)(uri)};

    ret = CmGetCertInfo(&uriBlob, CM_SYSTEM_TRUSTED_STORE, &certInfo);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetSytemRootCertData, get cert info failed, ret = %d ", ret);
        FreeCMBlobData(&(certInfo.certInfo));
        FreeCertList(certList);
        return CM_FAILURE;
    }

    if (memcpy_s(certData, MAX_LEN_CERTIFICATE, certInfo.certInfo.data, certInfo.certInfo.size) != 0) {
        DBG2(DBG_CFG, "rzc GetSytemRootCertData, memory copy failed");
        FreeCMBlobData(&(certInfo.certInfo));
        FreeCertList(certList);
        return CM_FAILURE;
    }

    FreeCMBlobData(&(certInfo.certInfo));
    FreeCertList(certList);
    return CM_SUCCESS;
}

int32_t GetVpnUserCertInfo(char *uri, uint8_t* certData)
{
    struct CertInfo certInfo;
    unsigned int len = sizeof(struct CertInfo);
    (void)memset(&certInfo, 0, len);
    int32_t ret = InitCertInfo(&certInfo);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetVpnUserCertInfo get init cert info failed, ret = %d",ret);
        FreeCMBlobData(&(certInfo.certInfo));
        return CM_FAILURE;
    }
    struct CmBlob uriBlob = {strlen(uri) + 1, (uint8_t *)(uri)};
    ret = CmGetCertInfo(&uriBlob, CM_SYS_CREDENTIAL_STORE, &certInfo);
    if (ret != CM_SUCCESS) {
        DBG2(DBG_CFG, "rzc GetVpnUserCertInfo get user cert info failed, ret = %d",ret);
        FreeCMBlobData(&(certInfo.certInfo));
        return CM_FAILURE;
    }

    if (memcpy(certData, certInfo.certInfo.data, certInfo.certInfo.size) != 0) {
        FreeCMBlobData(&(certInfo.certInfo));
        return CM_FAILURE;
    }
    return CM_SUCCESS;
}